/**
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the GPL License, Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.gnu.org/licenses/lgpl-3.0.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.guardianapis.api.client;

import java.io.InputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.guardianapis.api.exceptions.ContentException;
import com.guardianapis.api.http.GuardianHttpClient;
import com.guardianapis.api.http.GuardianHttpResponse;
import com.guardianapis.api.transformers.ItemRequestTransformer;
import com.guardianapis.api.transformers.SearchRequestTransformer;
import com.guardianapis.api.transformers.TagsRequestTransformer;
import com.guardianapis.api.util.StreamLogger;
import com.guardianapis.api.util.URLEncoder;

public class BaseGuardianContentClient implements GuardianContentClient {

	/*******************************************************************************
	 * Constants
	 *******************************************************************************/
	
	protected Log log = LogFactory.getLog(BaseGuardianContentClient.class);
	private static final String API_KEY_PARAM = "api_key";
	private static final char URL_PARAM_SEPERATOR_CHAR = GuardianConstants.URL_PARAM_SEPERATOR.charAt(0);
	
	/*******************************************************************************
	 * Properties
	 *******************************************************************************/
	
	protected URLEncoder urlEncoder;
	protected SearchRequestTransformer searchRequestTransformer;
	protected ItemRequestTransformer contentRequestTransformer;
	protected TagsRequestTransformer allSubjectsRequestTransformer;
	protected GuardianHttpClient httpClient;
	protected String apiBaseUrl;
	protected String apiSearchRelativeUrl;
	protected String apiItemRelativeUrl;
	protected String apiTagsRelativeUrl;
	protected String apiKey;

	/*******************************************************************************
	 * Constructors
	 *******************************************************************************/
	
	public BaseGuardianContentClient(GuardianHttpClient pHttpClient, String pApiKey) {
		super();

		// Creating the URL encoding utility object for encoding API Keys
		
		this.urlEncoder = new URLEncoder();
		
		// Setting parameter properties
		
		this.httpClient = pHttpClient;
		this.apiKey = pApiKey;
		
		// Creating transformers required to produce query strings
		
		this.searchRequestTransformer = new SearchRequestTransformer();
		this.contentRequestTransformer = new ItemRequestTransformer();
		this.allSubjectsRequestTransformer = new TagsRequestTransformer();
		
		// Set default relative URLs that can be overridden using setters e.g. for test configuration
		
		this.apiBaseUrl = GuardianConstants.DEFAULT_CONTENT_API_BASEURL;
		this.apiSearchRelativeUrl = GuardianConstants.DEFAULT_SEARCH_API_URL;
		this.apiItemRelativeUrl = GuardianConstants.DEFAULT_CONTENT_API_URL;
		this.apiTagsRelativeUrl = GuardianConstants.DEFAULT_ALL_SUBJECTS_API_URL;
	}

	/*******************************************************************************
	 * Implementing GuardianContentClient
	 *******************************************************************************/
	
	public InputStream searchRaw(SearchRequest pSearchRequest) throws ContentException {
		String methodParameters = this.searchRequestTransformer.transformToQueryString(pSearchRequest);
		String absoluteApiMethodUrl = generateApiUrl(this.apiSearchRelativeUrl, methodParameters);
		
		log.debug("Requesting search using url: " + absoluteApiMethodUrl);
		GuardianHttpResponse response = this.httpClient.requestUrl(absoluteApiMethodUrl);
		
		if (response.isSuccess()) {
			if (log.isDebugEnabled()) {
				log.debug("Returned stream: " + StreamLogger.getStreamCharacters(response.getResponse(), GuardianConstants.NUM_CHARACTERS_TO_LOG_FROM_STREAM));
			}
			return response.getResponse();
		}
		else {
			throw new ContentException(response.getErrorMessage(), response.getErrorCause());
		}
	}

	public InputStream itemRaw(ItemRequest pItemRequest) throws ContentException {
		String methodParameters = this.contentRequestTransformer.transformToQueryString(pItemRequest);
		String absoluteApiMethodUrl = generateApiUrl(this.apiItemRelativeUrl, methodParameters);
		
		log.debug("Requesting content using url: " + absoluteApiMethodUrl);
		GuardianHttpResponse response = this.httpClient.requestUrl(absoluteApiMethodUrl);
		
		if (response.isSuccess()) {
			if (log.isDebugEnabled()) {
				log.debug("Returned stream: " + StreamLogger.getStreamCharacters(response.getResponse(), GuardianConstants.NUM_CHARACTERS_TO_LOG_FROM_STREAM));
			}
			return response.getResponse();
		}
		else {
			throw new ContentException(response.getErrorMessage(), response.getErrorCause());
		}
	}

	public InputStream tagsRaw(TagsRequest pTagsRequest) throws ContentException {
		String methodParameters = this.allSubjectsRequestTransformer.transformToQueryString(pTagsRequest);
		String absoluteApiMethodUrl = generateApiUrl(this.apiTagsRelativeUrl, methodParameters);
		
		log.debug("Requesting content using url: " + absoluteApiMethodUrl);
		GuardianHttpResponse response = this.httpClient.requestUrl(absoluteApiMethodUrl);
		
		if (response.isSuccess()) {
			if (log.isDebugEnabled()) {
				log.debug("Returned stream: " + StreamLogger.getStreamCharacters(response.getResponse(), GuardianConstants.NUM_CHARACTERS_TO_LOG_FROM_STREAM));
			}
			return response.getResponse();
		}
		else {
			throw new ContentException(response.getErrorMessage(), response.getErrorCause());
		}
	}

	/*******************************************************************************
	 * Protected Methods
	 *******************************************************************************/
	
	protected String generateApiUrl(String pMethodPath, String pMethodParameters) {
		StringBuilder apiUrl = new StringBuilder(this.apiBaseUrl);
		String apiKeyQueryString = generateApiKeyQueryString();
		
		apiUrl.append(GuardianConstants.URL_PATH_SEPERATOR);
		apiUrl.append(pMethodPath);
		
		if (pMethodParameters.charAt(0) != URL_PARAM_SEPERATOR_CHAR) {
			// The method parameters actually form part of the main body of the URL, so add them first, then add the key afterwards
			
			apiUrl.append(pMethodParameters);
			if (pMethodParameters.indexOf(GuardianConstants.URL_PARAM_INITIALIZER) == -1) {
				apiUrl.append(GuardianConstants.URL_PARAM_INITIALIZER);
			}
			else {
				apiUrl.append(GuardianConstants.URL_PARAM_SEPERATOR);
			}
			apiUrl.append(apiKeyQueryString);
		} else {
			// The method parameters are actually query string components so add the key, then append the method parameters
			
			apiUrl.append(GuardianConstants.URL_PARAM_INITIALIZER);
			apiUrl.append(apiKeyQueryString);
			apiUrl.append(pMethodParameters);
		}
		
		if (log.isDebugEnabled()) {
			log.debug("Returning the full API url: " + apiUrl.toString());
		}
		
		return apiUrl.toString();
	}

	protected String generateApiKeyQueryString() {
		return API_KEY_PARAM + "=" + urlEncoder.encode(this.apiKey);
	}

	/*******************************************************************************
	 * Getters / Setters
	 *******************************************************************************/
	
	public String getApiBaseUrl() {
		return this.apiBaseUrl;
	}

	public void setApiBaseUrl(String pApiBaseUrl) {
		this.apiBaseUrl = pApiBaseUrl;
	}

	public String getApiSearchRelativeUrl() {
		return this.apiSearchRelativeUrl;
	}

	public void setApiSearchRelativeUrl(String pApiSearchRelativeUrl) {
		this.apiSearchRelativeUrl = pApiSearchRelativeUrl;
	}

	public String getApiItemRelativeUrl() {
		return this.apiItemRelativeUrl;
	}

	public void setApiItemRelativeUrl(String pApiContentRelativeUrl) {
		this.apiItemRelativeUrl = pApiContentRelativeUrl;
	}

	public String getApiTagsRelativeUrl() {
		return this.apiTagsRelativeUrl;
	}

	public void setApiTagsRelativeUrl(String pApiAllSubjectsRelativeUrl) {
		this.apiTagsRelativeUrl = pApiAllSubjectsRelativeUrl;
	}

	public String getApiKey() {
		return this.apiKey;
	}

	public void setApiKey(String pApiKey) {
		this.apiKey = pApiKey;
	}

	public GuardianHttpClient getHttpClient() {
		return this.httpClient;
	}

	public void setHttpClient(GuardianHttpClient pHttpClient) {
		this.httpClient = pHttpClient;
	}

}